/*
 * Copyright (C) 2010 Jakub Miara
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using System.Reflection;
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell.Settings;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using System.Windows.Forms;

namespace Net.Araim.FavouriteLocations
{

    /// <summary>
    /// Main package class
    /// </summary>
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [ProvideMenuResource(1000, 1)]
    [Guid("D2BC0DE2-7148-48BA-9099-2E679A991E6B")]
    [ComVisible(true)]
    public sealed class FavLocationsPkg : Package
    {
        #region Member Variables
        HashSet<FavouriteLocation> locations = new HashSet<FavouriteLocation>(new FavouriteLocationComparer());

        private static string FavConfigCollection = "FavConfigCollection";
        private static string FavConfigProperty = "FavConfigPropertyStream";
        #endregion


        /// <summary>
        /// Initializes a new instance of the <see cref="FavLocationsPkg"/> class.
        /// </summary>
        public FavLocationsPkg()
        {
            Trace.WriteLine(String.Format(CultureInfo.CurrentUICulture, "Entering constructor for: {0}", this.ToString()));
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override void Initialize()
        {
            Trace.WriteLine(String.Format(CultureInfo.CurrentUICulture, "Entering Initialize() of: {0}", this.ToString()));
            base.Initialize();


            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if (null != mcs)
            {

                SettingsManager settingsManager = new ShellSettingsManager(this);
                SettingsStore configurationSettingsStore = settingsManager.GetReadOnlySettingsStore(SettingsScope.UserSettings);
                try
                {
                    if (configurationSettingsStore.PropertyExists(FavConfigCollection, FavConfigProperty))
                    {
                        MemoryStream ms = new MemoryStream();
                        XmlSerializer s = new XmlSerializer(typeof(HashSet<FavouriteLocation>));
                        this.locations = s.Deserialize(configurationSettingsStore.GetMemoryStream(FavConfigCollection, FavConfigProperty)) as HashSet<FavouriteLocation>;
                        ms.Close();

                    }
                }
                catch (Exception e)
                {
                    this.locations = new HashSet<FavouriteLocation>();
                }

                CommandID id = new CommandID(GuidsList.favLocationsCmdSet, PkgCmdIDList.cmdidAddFav);
                OleMenuCommand command = new OleMenuCommand(new EventHandler(AddFavCommandCallback), id);
                mcs.AddCommand(command);

                CommandID manageFavs = new CommandID(GuidsList.favLocationsCmdSet, PkgCmdIDList.cmdidManageFavs);
                OleMenuCommand manageFavsCommand = new OleMenuCommand(new EventHandler(ManageFavCommandCallback), manageFavs);
                mcs.AddCommand(manageFavsCommand);

                CommandID comboclick = new CommandID(GuidsList.favLocationsCmdSet, PkgCmdIDList.cmdidFavsList);
                OleMenuCommand combocommand = new OleMenuCommand(new EventHandler(SelectFavCommandCallback), comboclick);
                mcs.AddCommand(combocommand);


                CommandID menuMyDropDownComboGetListCommandID = new CommandID(GuidsList.favLocationsCmdSet, (int)PkgCmdIDList.cmdidListFavs);

                MenuCommand menuMyDropDownComboGetListCommand =
                  new OleMenuCommand(new EventHandler(OnMenuMyDropDownComboGetList),
                  menuMyDropDownComboGetListCommandID);
                mcs.AddCommand(menuMyDropDownComboGetListCommand);
            }
        }

        #region Commands Actions

        /// <summary>
        /// Selects the fav command callback.
        /// </summary>
        /// <param name="caller">The caller.</param>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Microsoft.Samples.VisualStudio.MenuCommands.MenuCommandsPackage.OutputCommandString(System.String)")]
        private void SelectFavCommandCallback(object caller, EventArgs args)
        {
            if (args is Microsoft.VisualStudio.Shell.OleMenuCmdEventArgs)
            {
                var cmdArgs = (args as Microsoft.VisualStudio.Shell.OleMenuCmdEventArgs);
                if (cmdArgs.InValue != null)
                {
                    lock (this.locations)
                    {
                        foreach (var loc in locations)
                        {
                            if (loc.Name == (string)cmdArgs.InValue)
                            {
                                SetPath(loc.Path);
                                return;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Manages the fav command callback.
        /// </summary>
        /// <param name="caller">The caller.</param>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Microsoft.Samples.VisualStudio.MenuCommands.MenuCommandsPackage.OutputCommandString(System.String)")]
        private void ManageFavCommandCallback(object caller, EventArgs args)
        {

            if (this.locations == null)
            {
                this.locations = new HashSet<FavouriteLocation>();
            }
            lock (this.locations)
            {
                var manageFavsDialog = new ManageFavs(this.locations);
                if (manageFavsDialog.ShowDialog() == DialogResult.OK)
                {
                    this.locations = manageFavsDialog.FavList;
                    SaveFavs();
                }
            }


        }

        /// <summary>
        /// Saves the favs.
        /// </summary>
        private void SaveFavs()
        {
            SettingsManager settingsManager = new ShellSettingsManager(this);
            WritableSettingsStore configurationSettingsStore = settingsManager.GetWritableSettingsStore(SettingsScope.UserSettings);

            if (!configurationSettingsStore.CollectionExists(FavConfigCollection))
            {
                configurationSettingsStore.CreateCollection(FavConfigCollection);
            }


            MemoryStream ms = new MemoryStream();

            XmlSerializer s = new XmlSerializer(typeof(HashSet<FavouriteLocation>));
            s.Serialize(ms, this.locations);
            configurationSettingsStore.SetMemoryStream(FavConfigCollection, FavConfigProperty, ms);
            ms.Close();
            s = null;
        }


        /// <summary>
        /// Adds the fav command callback.
        /// </summary>
        /// <param name="caller">The caller.</param>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Microsoft.Samples.VisualStudio.MenuCommands.MenuCommandsPackage.OutputCommandString(System.String)")]
        private void AddFavCommandCallback(object caller, EventArgs args)
        {

            if (this.locations == null)
            {
                this.locations = new HashSet<FavouriteLocation>();
            }

            AddFavourite dialog = new AddFavourite(GetPath());

            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {

                foreach (var loc in locations)
                {
                    if (loc.Name == dialog.FavName)
                    {
                        MessageBox.Show(string.Format("Favourite Location \"{0}\" already exists", dialog.FavName));
                        return;
                    }
                }

                lock (this.locations)
                {
                    this.locations.Add(new FavouriteLocation { Name = dialog.FavName, Path = dialog.FavLocation });
                }

                SaveFavs();


            }
        }

        /// <summary>
        /// Called when [menu my drop down combo get list].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnMenuMyDropDownComboGetList(object sender, EventArgs e)
        {
            if ((null == e) || (e == EventArgs.Empty))
            {
                // --- We should never get here; EventArgs are required.
                throw (new ArgumentNullException("Invalid argument"));
            }
            OleMenuCmdEventArgs eventArgs = e as OleMenuCmdEventArgs;
            if (eventArgs != null)
            {
                object inParam = eventArgs.InValue;
                IntPtr vOut = eventArgs.OutValue;
                if (inParam != null)
                {
                    //ignore
                }
                else if (vOut != IntPtr.Zero)
                {
                    if (this.locations != null)
                    {
                        lock (this.locations)
                        {
                            string[] locs = new string[this.locations.Count];
                            int i = 0;
                            foreach (var loc in this.locations)
                            {
                                locs[i++] = loc.Name;
                            }
                            Array.Sort<string>(locs);
                            Marshal.GetNativeVariantForObject(locs, vOut);
                        }
                    }
                    else
                    {
                        return;
                    }
                }
                else
                {
                    //ignoer
                }
            }
        }

        /// <summary>
        /// Gets the path.
        /// </summary>
        /// <returns></returns>
        private string GetPath()
        {

            DTE2 dte = GetService(typeof(SDTE)) as DTE2;
            VersionControlExt ext = dte.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt") as VersionControlExt;
            VersionControlExplorerExt vcee = ((Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExplorerExt)(ext.Explorer));
            return vcee.CurrentFolderItem.SourceServerPath;
        }

        /// <summary>
        /// Sets the path.
        /// </summary>
        /// <param name="path">The path.</param>
        private void SetPath(string path)
        {

            DTE2 dte = GetService(typeof(SDTE)) as DTE2;
            VersionControlExt ext = dte.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt") as VersionControlExt;
            VersionControlExplorerExt vcee = ((Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExplorerExt)(ext.Explorer));

            foreach (MemberInfo mi in vcee.GetType().GetMembers(BindingFlags.NonPublic | BindingFlags.Instance))
            {
                if (mi.Name == "m_sceToolWindow")
                {
                    FieldInfo fi = mi as FieldInfo;
                    object m_sceToolWindow = fi.GetValue(vcee);
                    foreach (MemberInfo mi2 in m_sceToolWindow.GetType().GetMembers(BindingFlags.NonPublic | BindingFlags.Instance))
                    {
                        if (mi2.Name == "SccExplorer")
                        {
                            PropertyInfo pi = mi2 as PropertyInfo;
                            object sccExplorer = pi.GetValue(m_sceToolWindow, null);
                            pi = sccExplorer.GetType().GetProperty("CurrentServerPath");
                            pi.SetValue(sccExplorer, path, null);
                        }
                    }
                }
            }
        }
        #endregion
    }
}
